#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <linux/gpio.h>
#include <fcntl.h>

#include "gpiod.h"

#define  GPIO_INFO_NUM					3

typedef struct gpio_info
{
    const char     *name;
    int            chip_num;
    int            gpio_num; 
    bool           active;
    enum gpiod_line_direction  direct;
    struct gpiod_line_request *request;
}gpio_info;

static gpio_info rc8088_gipo[GPIO_INFO_NUM] =
{
    {"chipen",1,68,false,GPIOD_LINE_DIRECTION_OUTPUT,NULL},//(G26)
    {"irq0",1,15,false,GPIOD_LINE_DIRECTION_INPUT,NULL},//(R22)
	{"irq1",1,34,false,GPIOD_LINE_DIRECTION_INPUT,NULL},//(N23)
};

int release_gpio()
{
    int		i;
	
	printf("release_gpio gpios\n");

	for(i=0; i<GPIO_INFO_NUM; i++)
	{
		if( rc8088_gipo[i].request )
		{
            if(rc8088_gipo->direct ==GPIOD_LINE_DIRECTION_OUTPUT){
                	gpiod_line_request_set_value(rc8088_gipo[i].request, rc8088_gipo[i].gpio_num, GPIOD_LINE_VALUE_INACTIVE);
            }
    		gpiod_line_request_release(rc8088_gipo->request);
		}

    }
}

int bsp_gpio_init()
{
 	char				chip_dev[32];
    int				    i,rv = 0;
	struct gpiod_chip		    *chip;
	struct gpiod_line_settings	*settings;
	struct gpiod_line_config	*line_cfg;
	struct gpiod_request_config	*req_cfg;

    /*struct gpiod_line_settings
	{
		enum gpiod_line_direction	direction;		设置 GPIO 线的方向(输入/输出)
		enum gpiod_line_edge		edge_detection;		设置 GPIO 线的边沿检测，用于捕获信号变化
		enum gpiod_line_drive		drive;			设置 GPIO 线的驱动模式
		enum gpiod_line_bias		bias;			设置 GPIO 线的偏置
		bool 				active_low;		设置 GPIO 线是否为低电平有效
		enum gpiod_line_clock		event_clock;		设置事件时钟，用于时间戳事件
		long				debounce_period_us;	设置去抖动的时间，以微秒为单位
		enum gpiod_line_value		output_value;		设置 GPIO 线的输出值
	};*/	
 
	settings = gpiod_line_settings_new();
	if( !settings )
	{
		printf("unable to allocate line settings\n");
		rv = -2;
		goto cleanup;
	}
 
	/*struct gpiod_line_config
	{
		struct per_line_config	line_configs[LINES_MAX];	配置每条 GPIO 线的特性，如方向、边沿检测、驱动模式、偏置等
		size_t			num_configs;			指示 line_configs 数组中有多少个有效的 GPIO 线配置信息
		enum gpiod_line_value   output_values[LINES_MAX];	设置每条 GPIO 线的输出值，如高电平、低电平
		size_t 			num_output_values;		指示 output_values 数组中有多少个有效的 GPIO 线输出值
		struct settings_node	*sref_list;			用于管理 GPIO 线的设置信息，如方向、边沿检测、驱动模式等
	};*/
 
	line_cfg = gpiod_line_config_new();
	if( !line_cfg )
	{
		printf("unable to allocate the line config structure");
		rv = -2;
		goto cleanup;
	}
 
	/*struct gpiod_request_config
	{
		char		consumer[GPIO_MAX_NAME_SIZE];		标识请求 GPIO 引脚的应用程序或模块
		size_t		event_buffer_size;			指定用于存储事件数据的缓冲区大小
	};*/
 
	req_cfg = gpiod_request_config_new();
	if( !req_cfg )
	{
		printf("unable to allocate the request config structure");
		rv = -2;
		goto cleanup;
	}

    for(i=0;i<GPIO_INFO_NUM;i++)
    {
        snprintf(chip_dev, sizeof(chip_dev), "/dev/gpiochip%d", rc8088_gipo[i].chip_num);
        chip = gpiod_chip_open(chip_dev);
        if( !chip )
        {
            printf("open gpiochip failure, maybe you need running as root\n");
            rv = -3;
            goto cleanup;
        }
	
		//输出IO初始化
		if(rc8088_gipo[i].direct == GPIOD_LINE_DIRECTION_OUTPUT){
			gpiod_line_settings_reset(settings);
			gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
			gpiod_line_settings_set_active_low(settings, &rc8088_gipo[i].active);
			gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_ACTIVE);

		}else if(rc8088_gipo[i].direct == GPIOD_LINE_DIRECTION_INPUT){
			gpiod_line_settings_reset(settings);
			gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
			gpiod_line_settings_set_edge_detection(settings, GPIOD_LINE_EDGE_RISING);
        	gpiod_line_settings_set_debounce_period_us(settings, 200000); /// New added for debounce
		}
       
		gpiod_line_config_reset(line_cfg);
		gpiod_line_config_add_line_settings(line_cfg, &rc8088_gipo[i].gpio_num, 1, settings);
	
        gpiod_request_config_set_consumer(req_cfg,rc8088_gipo[i].name);

        rc8088_gipo[i].request = gpiod_chip_request_lines(chip, req_cfg, line_cfg);

        gpiod_chip_close(chip);
        
        if(!rc8088_gipo[i].request)
        {
            printf("err request!\n");
            rv = -5;
            goto cleanup;
        }

    }

cleanup:
	if( rv < 0 )
		release_gpio(rc8088_gipo);
 
	if( line_cfg )
		gpiod_line_config_free(line_cfg);
 
	if( req_cfg )
		gpiod_request_config_free(req_cfg);
	
	if( settings )
		gpiod_line_settings_free(settings);
 
	return rv;

}

int chip_en_8088()
{
    return gpiod_line_request_set_value(rc8088_gipo[0].request, rc8088_gipo[0].gpio_num, GPIOD_LINE_VALUE_INACTIVE);
}

int chip_disen_8088()
{
    return gpiod_line_request_set_value(rc8088_gipo[0].request, rc8088_gipo[0].gpio_num, GPIOD_LINE_VALUE_ACTIVE);
}


static const char *edge_event_type_str(struct gpiod_edge_event *event)
{
	switch (gpiod_edge_event_get_event_type(event)) {
	case GPIOD_EDGE_EVENT_RISING_EDGE:
		return "Rising";
	case GPIOD_EDGE_EVENT_FALLING_EDGE:
		return "Falling";
	default:
		return "Unknown";
	}
}


int read_irq0()
{
	/* Example configuration - customize to suit your situation. */
	struct gpiod_edge_event_buffer *event_buffer;
	struct gpiod_edge_event *event;
	int i, ret, event_buf_size;

	/*
	 * A larger buffer is an optimisation for reading bursts of events from
	 * the kernel, but that is not necessary in this case, so 1 is fine.
	 */
// 	init_gpio();
	event_buf_size = 1;
	event_buffer = gpiod_edge_event_buffer_new(event_buf_size);
	if (!event_buffer) {
		fprintf(stderr, "failed to create event buffer: %s\n",
			strerror(errno));
		return EXIT_FAILURE;
	}

	for (;;) {
		/* Blocks until at least one event is available. */
		ret = gpiod_line_request_read_edge_events(rc8088_gipo[1].request, event_buffer,
							  event_buf_size);
		if (ret == -1) {
			fprintf(stderr, "error reading edge events: %s\n",
				strerror(errno));
			return EXIT_FAILURE;
		}
		for (i = 0; i < ret; i++) {
			event = gpiod_edge_event_buffer_get_event(event_buffer,i);
			printf("offset: %d  type: %-7s  event #%ld\n",
			       gpiod_edge_event_get_line_offset(event),
			       edge_event_type_str(event),
			       gpiod_edge_event_get_line_seqno(event));
		}
	}

	return 0;
}